package com.bsd.xxyp.rdlock.handler;

import com.alibaba.fastjson.JSON;
import com.bsd.xxyp.rdlock.annotation.RdLock;
import com.bsd.xxyp.rdlock.context.LockHolder;
import com.bsd.xxyp.rdlock.entity.LockInfo;
import com.bsd.xxyp.rdlock.entity.ResultRecord;
import com.bsd.xxyp.rdlock.exception.RdLockException;
import com.bsd.xxyp.rdlock.executor.LockExecutor;
import com.bsd.xxyp.rdlock.executor.ZkCuratorLockExecutor;
import com.bsd.xxyp.rdlock.parse.LockKeyProcessor;
import com.bsd.xxyp.rdlock.context.LockAnnotationDataExtractor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author ：wangcheng
 * @date 2022/12/12
 **/
@Aspect
@Slf4j
public class RdLockHandler {

    private LockKeyProcessor lockKeyProcessor;
    private LockHolder lockHolder;
    @Autowired(required = false)
    private LockResultHandler lockResultHandler;

    public RdLockHandler(LockHolder lockHolder,LockKeyProcessor lockKeyProcessor) {
        this.lockHolder = lockHolder;
        this.lockKeyProcessor = lockKeyProcessor;
    }

    @Around(value = "@annotation(com.bsd.xxyp.rdlock.annotation.RdLock)")
    public Object lockHandler(ProceedingJoinPoint pjp) throws Throwable {
        LockAnnotationDataExtractor extractor = new LockAnnotationDataExtractor(pjp);
        String lockKey = lockKeyProcessor.buildLockKey(extractor);
        RdLock rdLock = extractor.getRdLock();
        LockExecutor lockExecutor = lockHolder.obtainLockExecutor(rdLock.executor());
        boolean lockResult = false;
        LockInfo lockInfo = lockHolder.buildLockInfo(lockKey, rdLock.retryInterval(), rdLock.lockTimeOut());
        try {
            if (rdLock.attemptedWait()) {
                for (int i = 0; i <rdLock.retryCount(); i++) {
                    lockResult =  lockExecutor.tryAcquire(lockInfo);
                    log.info("rdLock lockSuccess: {} , tryCount; {} ,lockKey: {} ,executor: {} ",
                            lockResult,i+1,lockKey,lockExecutor.getClass().getSimpleName());
                    if (lockResult) break;
                }
            }else {
                lockInfo.setWaitTime(0L);
                lockResult = lockExecutor.tryAcquire(lockInfo);
                log.info("rdLock lockSuccess: {} ,lockKey: {} ,executor: {} ",
                        lockResult,lockKey,lockExecutor.getClass().getSimpleName());
            }
            lockInfo.setLockSuccess(lockResult);
            if (!lockResult) {
                throw new RdLockException(rdLock.errMsg());
            }
            return pjp.proceed();
        } finally {
            if (rdLock.autoRelease() || lockExecutor.equals(ZkCuratorLockExecutor.class)) {
                boolean releaseLock = lockExecutor.releaseLock(lockInfo);
                log.info("rdLock releaseSuccess: {} ,lockKey: {} ,executor: {} ",
                        releaseLock,lockKey,lockExecutor.getClass().getSimpleName());
            }else {
                log.info("rdLock waitToLockTimeout ,releaseSuccess: {} ,lockKey: {} ,executor: {} ",
                        Boolean.FALSE,lockKey,lockExecutor.getClass().getSimpleName());
            }
            if (lockResultHandler !=null) {
                ResultRecord resultRecord = new ResultRecord(extractor, lockExecutor.getClass().getSimpleName());
                resultRecord.setLockKey(lockKey);
                resultRecord.setLockResult(lockResult);
                lockResultHandler.record(resultRecord);
            }
        }
    }
}
